﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;

namespace SynchronizedPlayer {
	class Controller {
		private const int DEFAULT_PORT = 23232;
		private const string DEFAULT_ADDRESS = "127.0.0.1";

		public ConcurrentQueue<Command> InCommands { get; private set; }

		public NetReceiver Receiver { get; private set; }
		public NetSender Sender { get; private set; }
		private Timer _executor;

		Dictionary<Type, LinkedList<Action<object[]>>> Callbacks = new Dictionary<Type, LinkedList<Action<object[]>>>();

		private bool _work = true;

		public Controller(int listenPort = DEFAULT_PORT, string IP = DEFAULT_ADDRESS, int connectPort = DEFAULT_PORT) {
			//IP = "192.168.211.47";
			Setup(listenPort, IP, connectPort);

			foreach (var type in Command.AllCommands) {
				Callbacks.Add(type.Value, new LinkedList<Action<object[]>>());
			}
		}


		public void Setup(int listenPort, string toIP, int toPort) {
			if (Receiver != null) {
				Receiver.Dispose();
			}
			if (Sender != null) {
				Sender.Dispose();
			}

			InCommands = new ConcurrentQueue<Command>();
			Sender = new NetSender(toIP, toPort);
			Receiver = new NetReceiver(listenPort, InCommands);
			_executor = new Timer(new TimerCallback(ExecuteLoop), null, 0, 100);
		}

		public void Subscribe(Type t, Action<object[]> act) {
			Callbacks[t].AddLast(act);
		}

		public void SendPlaylist(string currentTrack, string position, string[] items) {
			Sender.Enqueue(new CMDPlaylistBegin(currentTrack, position));
			foreach (var item in items) {
				Sender.Enqueue(new CMDPlaylistItem(item));
			}
			Sender.Enqueue(new CMDPlaylistEnd());
		}

		private void ExecuteLoop(object o) {
			lock (this) {
				if (!_work) {
					_executor.Dispose();
				} else {
					Command next;

					if (InCommands.TryDequeue(out next)) {
						dynamic nextcmd = next;
						object[] execResult = Execute(nextcmd);
						foreach (var act in Callbacks[nextcmd.GetType()]) {
							act(execResult);
						}
					}
				}
			}
		}

		private object[] Execute(CMDPlaylistBegin c) {
			object[] result = new object[3];
			List<string> items = new List<string>();
			bool end = false;
			
			Debug.WriteLine("Playlist started. current track {0} position {1}", c.CurrentTrack, c.Position);
			
			while (!end) {
				Command next;
				
				while (InCommands.TryDequeue(out next) && next is CMDPlaylistItem) {
					Debug.WriteLine(next.ToString());
					items.Add((next as CMDPlaylistItem).Hash);
				}
				
				if (next is CMDPlaylistEnd) {
					Debug.WriteLine("Hey, it ended!");
					end = true;
				}
			}

			result[0] = Convert.ToInt32(c.CurrentTrack);
			result[1] = Convert.ToDouble(c.Position);
			result[2] = items;
			Debug.WriteLine(result);
			
			return result;
		}

		public void Dispose() {
			_work = false;
			Receiver.Dispose();
			Sender.Dispose();
		}

	}
}
